package com.bank.manage.config;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.bank.manage.constance.BankConstance;
import com.bank.manage.enumerate.BankRedisKey;
import com.bank.manage.enumerate.BankStatus;
import com.bank.manage.model.app.SessionKeyReq;
import com.bank.manage.model.app.SessionKeyRes;
import com.bank.manage.utils.Rsa2Utils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.gavin.core.attribute.AppEncryptConfig;
import org.gavin.core.constant.CommonConst;
import org.gavin.core.enums.CommonStatus;
import org.gavin.core.enums.SafetyType;
import org.gavin.core.exception.SafetyException;
import org.gavin.core.service.safety.SafetyAbstract;
import org.gavin.core.utils.GMSM4Utils;
import org.gavin.core.utils.JsonUtils;
import org.redis.service.RedisService;
import org.springframework.stereotype.Component;

import java.util.UUID;

/**
 * AppSafety
 *
 * @author grl
 * @date 2024/5/9
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class AppSafety implements SafetyAbstract {

    private final HttpServletRequest request;
    private final HttpServletResponse response;
    private final RedisService redisService;
    private final AppEncryptConfig appEncryptConfig;


    @Override
    public SafetyType TYPE() {
        return SafetyType.APP;
    }

    @Override
    public String getEncrypt(String data) {
        String clientPubKey = "";
        String appId = request.getHeader(CommonConst.SAFETY_KEY);
        if (StrUtil.hasBlank(appId)) {
            throw SafetyException.le(BankStatus.UNIQUE_VALUE_EMPTY, BankConstance.MODEL_NAME, data);
        }
        //获取解密私钥
        BankRedisKey apiAccessRsaSecond = BankRedisKey.API_ACCESS_RSA_SECOND;
        apiAccessRsaSecond.setKey(appId);
        clientPubKey = redisService.get(apiAccessRsaSecond);
        if (StrUtil.isBlank(clientPubKey)) {
            throw SafetyException.le(CommonStatus.LACK_HEADER_PARAM_ERROR);
        }
        BankRedisKey apiAccessFrontendRsa2Key = BankRedisKey.API_ACCESS_FRONTEND_RSA2_KEY;
        apiAccessFrontendRsa2Key.setKey(appId);
        String sessionKeyResString = redisService.get(apiAccessFrontendRsa2Key);
        SessionKeyRes sessionKeyRes;
        if (StrUtil.isBlank(sessionKeyResString)) {
            try {
                String paramsKey = UUID.randomUUID().toString().replaceAll("-", "");
                String rsaEncryptKey = Rsa2Utils.encrypt(paramsKey, clientPubKey);
                sessionKeyRes = new SessionKeyRes();
                sessionKeyRes.setParamsEncryptKey(rsaEncryptKey);
                sessionKeyRes.setParamsDecrptKey(paramsKey);
                apiAccessFrontendRsa2Key.setValue(JsonUtils.toJsonString(sessionKeyRes));
                apiAccessFrontendRsa2Key.setTime(appEncryptConfig.getSessionTimeout());
                redisService.add(apiAccessFrontendRsa2Key);
            } catch (Exception e) {
                log.error("加密失败", e);
                throw SafetyException.le(CommonStatus.CIPHERTEXT_HANDLE_FAILED);
            }
        } else {
            sessionKeyRes = JSONObject.parseObject(sessionKeyResString, SessionKeyRes.class);
        }
        response.addHeader(CommonConst.UNIQUE_VALUE, sessionKeyRes.getParamsEncryptKey());
        response.addHeader(CommonConst.SAFETY_KEY, appId);
        String params = GMSM4Utils.encryptEcb(data, sessionKeyRes.getParamsDecrptKey());
        if (appEncryptConfig.isShowEncryptLog()) {
            log.info("加密数据加密key：{}，加密数据解密key：{}", sessionKeyRes.getParamsEncryptKey(), sessionKeyRes.getParamsDecrptKey());
            log.info("加密前数据：{}，加密后数据：{}", data, params);
        }
        return params;
    }

    @Override
    public String getDecrypt(String data) {
        String appId = request.getHeader(CommonConst.SAFETY_KEY);
        String paramsKey = request.getHeader(CommonConst.UNIQUE_VALUE);
        if (StrUtil.hasBlank(appId, paramsKey)) {
            throw SafetyException.le(BankStatus.UNIQUE_VALUE_EMPTY, BankConstance.MODEL_NAME, data);
        }
        // 取出Redis中的rsa秘钥
        BankRedisKey apiAccessRsaFirst = BankRedisKey.API_ACCESS_RSA_FIRST;
        apiAccessRsaFirst.setKey(appId);
        String paramsDecrptPriKey = redisService.get(apiAccessRsaFirst);

        if (StrUtil.isBlank(paramsDecrptPriKey)) {
            log.error("从redis中获取解密私钥失败");
            throw SafetyException.le(CommonStatus.JSON_ERROR, BankConstance.MODEL_NAME, appId);
        }
        BankRedisKey apiAccessBackendRsa2Key = BankRedisKey.API_ACCESS_BACKEND_RSA2_KEY;
        apiAccessBackendRsa2Key.setKey(appId);
        String sessionKeyReqString = redisService.get(apiAccessBackendRsa2Key);
        SessionKeyReq sessionKeyReq = null;
        if (StrUtil.isBlank(sessionKeyReqString)) {
            String rsaDecryptKey = Rsa2Utils.decrypt(paramsDecrptPriKey, paramsKey);
            sessionKeyReq = new SessionKeyReq();
            sessionKeyReq.setParamsEncryptKey(paramsKey);
            sessionKeyReq.setParamsDecrptKey(rsaDecryptKey);
            apiAccessBackendRsa2Key.setTime(appEncryptConfig.getSessionTimeout());
            apiAccessBackendRsa2Key.setValue(JSONObject.toJSONString(sessionKeyReq));
            redisService.add(apiAccessBackendRsa2Key);
        } else {
            sessionKeyReq = JSONObject.parseObject(sessionKeyReqString, SessionKeyReq.class);
            if (StrUtil.isNotBlank(sessionKeyReq.getParamsDecrptKey()) || !sessionKeyReq.getParamsDecrptKey().equals(paramsKey)) {
                String rsaDecryptKey = Rsa2Utils.decrypt(paramsDecrptPriKey, paramsKey);
                sessionKeyReq.setParamsEncryptKey(paramsKey);
                sessionKeyReq.setParamsDecrptKey(rsaDecryptKey);
                apiAccessBackendRsa2Key.setTime(appEncryptConfig.getSessionTimeout());
                apiAccessBackendRsa2Key.setValue(JSONObject.toJSONString(sessionKeyReq));
                redisService.add(apiAccessBackendRsa2Key);
            }
        }
        try {
            String params = GMSM4Utils.decryptEcb(data, sessionKeyReq.getParamsDecrptKey());
            if (appEncryptConfig.isShowEncryptLog()) {
                log.info("解密数据加密key：{}，解密数据解密key：{}", sessionKeyReq.getParamsEncryptKey(), sessionKeyReq.getParamsDecrptKey());
                log.info("解密前数据：{}，解密后数据：{}", params);
            }
            data = params;
        } catch (Exception e) {
            e.printStackTrace();
            throw SafetyException.le(CommonStatus.JSON_ERROR, BankConstance.MODEL_NAME, data);
        }
        response.addHeader(CommonConst.SAFETY_KEY, appId);
        return data;
    }
}
